﻿
using System;
using System.Collections.Generic;
using System.Text;

namespace LExperiment.NetworkLib
{
    public static class CmdConstant
    {
        public const ushort Start = 1;
        public const ushort Stop = 2;
    }

    public class IdCmdBase
    {
        private IList<int> m_subscriberIds = new List<int>();

        public void AddSubscriberId(int id) { m_subscriberIds.Add(id); }
        public ICollection<int> SubscriberIds { get { return m_subscriberIds; } }

        //------------------------------------------------------//
        #region [ marshal and un-marshal ]

        public virtual int BinaryLength
        {
            get { return (m_subscriberIds.Count + 1) * 4; }
        }

        public virtual byte[] BinaryImage
        {
            get
            {
                byte[] binImage = new byte[this.BinaryLength];

                int pos = 0;
                BufferUtility.CopyBytes(m_subscriberIds.Count, binImage, pos);
                pos += 4;

                foreach (int id in m_subscriberIds)
                {
                    BufferUtility.CopyBytes(id, binImage, pos);
                    pos += 4;
                }

                return binImage;
            }
        }

        public virtual int Initialize(byte[] srcImage, int startIndex)
        {
            if (m_subscriberIds.Count != 0)
                throw new InvalidOperationException("previous id list still left");

            int pos = startIndex;
            int numIds = BufferUtility.ToInt32(srcImage, pos);
            pos += 4;

            for (int index = 0; index < numIds; ++index)
            {
                int id = BufferUtility.ToInt32(srcImage, pos);
                pos += 4;

                m_subscriberIds.Add(id);
            }

            return pos - startIndex;
        }

        #endregion

        //------------------------------------------------------//
        #region [ override object's virtual methods ]

        public override bool Equals(object obj)
        {
            if (obj == null) return false;
            if (object.ReferenceEquals(this, obj)) return true;
            if (this.GetType() != obj.GetType()) return false;

            IdCmdBase otherCmd = (IdCmdBase)obj;
            return IsIdListEqual(m_subscriberIds, otherCmd.m_subscriberIds);
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        private static bool IsIdListEqual(IList<int> idlist1, IList<int> idlist2)
        {
            if (idlist1 == null && idlist2 == null)
                return true;
            else if (idlist1 != null && idlist2 != null)
            {
                int numIds = idlist1.Count;
                if (idlist2.Count != numIds)
                    return false;
                else
                {
                    for (int index = 0; index < numIds; ++index)
                    {
                        if (idlist1[index] != idlist2[index])
                            return false;
                    }
                    return true;
                }
            }
            else
                return false;
        }

        #endregion
    }

    public sealed class StartCommand : IdCmdBase
    {
        // ************************************** //
        #region [ member variables ]

        private string m_pubAddress;
        private int m_pubPort;

        #endregion

        // ************************************** //
        #region [ member field operation ]

        public string PubAddress { get { return m_pubAddress; } set { m_pubAddress = value; } }
        public int PubPort { get { return m_pubPort; } set { m_pubPort = value; } }

        #endregion

        // ************************************** //
        #region [ marshal and un-marshal ]

        public override int BinaryLength
        {
            get
            {
                int length = 4;// length of number bytes for publisher addresses
                length += Encoding.ASCII.GetByteCount(m_pubAddress);// publisher address length

                length += 4;// publisher port length

                length += base.BinaryLength;

                return length;
            }
        }

        public override byte[] BinaryImage
        {
            get
            {
                byte[] binImage = new byte[this.BinaryLength];
                int pos = 0;

                // 1. publisher address
                byte[] addrBuffer = Encoding.ASCII.GetBytes(m_pubAddress);
                BufferUtility.CopyBytes(addrBuffer.Length, binImage, pos);
                pos += 4;

                Buffer.BlockCopy(addrBuffer, 0, binImage, pos, addrBuffer.Length);
                pos += addrBuffer.Length;

                // 2. publisher port
                BufferUtility.CopyBytes(m_pubPort, binImage, pos);
                pos += 4;

                // 3. subscriber ids
                Buffer.BlockCopy(base.BinaryImage, 0, binImage, pos, base.BinaryLength);

                return binImage;
            }
        }

        public override int Initialize(byte[] binImage, int startIndex)
        {
            int pos = startIndex;
            int length = BufferUtility.ToInt32(binImage, pos);
            pos += 4;

            m_pubAddress = Encoding.ASCII.GetString(binImage, pos, length);
            pos += length;

            m_pubPort = BufferUtility.ToInt32(binImage, pos);
            pos += 4;

            pos += base.Initialize(binImage, pos);
            return pos - startIndex;
        }

        #endregion

        // ************************************** //
        #region [ override object's virtual methods ]

        public override bool Equals(object obj)
        {
            if (obj == null)
                return false;

            if (object.ReferenceEquals(this, obj))
                return true;

            if (obj.GetType() != typeof(StartCommand))
                return false;

            StartCommand otherCmd = (StartCommand)obj;
            return (m_pubAddress.Equals(otherCmd.m_pubAddress))
                && (m_pubPort == otherCmd.m_pubPort)
                && base.Equals(obj);
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        #endregion
    }

    public sealed class StopCommand : IdCmdBase
    {
    }
}